/*
 * @Author: Oscar Pena
 * CS 431 2011
 * The terrain class sets the terrain size and the contains all the MapObjects 
 * that are located in the terrain. The terrain class assumes there is a wall 
 * around its boundaries. These walls prevents the Finch to go outside the terrain. 
 * The user can edit the size of the terrain as required.
 * */
package FlowGram.Application.SimulatorWindow.TerrainEditor;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import FlowGram.Application.SimulatorWindow.TerrainEditor.Direction;

public class Terrain {
	
	private int sizeX;
	private int sizeY;
	private List<MapObject> objs =  new ArrayList<MapObject>(); 
	
	public void SetSize(int x, int y){
	   // @Description     set the size of the terrain
	   // @Param    x width size in Tiles
	   // @Param    y height size in Tiles
		//@Note: in the Architectural document we anticipated to to track pixel sizes
		//		but at time of implementation keep track of tiles was preferred.
		this.sizeX = x;
		this.sizeY = y;
	}
	
	public void add(MapObject map){
		objs.add(map);
	}
	public void remove(MapObject map){
		
	}
	public Finch getFinch(){
		//@Description: traverses the List of Objects and returns the Finch
		//@Assumes: There is one Finch in the Objects list
		
		Finch returnFinch = null;	
		Iterator<MapObject> itr = objs.iterator();
		for(int i=0; i<objs.size(); i++){
			MapObject element = itr.next();
			if (element instanceof Finch) {
					return (Finch) element;
					}
		   	}
		return returnFinch;
	}
	public boolean detectCollision(Finch finch, boolean paint){
		//@Description: Traverses the List of objects and checks if there is walls in from of the finch
		//@Description: Two tiles need to be tested depending on the orientation of the finch
		boolean error = false;
		boolean returnfalse = false;
		Iterator<MapObject> itr = objs.iterator();
		for(int i=0; i<objs.size(); i++){
			MapObject element = itr.next();
			int x = element.getPositionX();
			int y = element.getPositionY();
			if (finch.getPositionX() <= x+1 && finch.getPositionX() >= x-1  && finch.getPositionY() <= y+1 && finch.getPositionY() >= y-1){
				
				if (element instanceof Wall){
					error = ((Wall)element).detectcoll(finch, paint);
					if (error==true){ returnfalse = true;}
				}
				}
		   	}
		return returnfalse;
	}
	public List<MapObject> getmapObjects(){
		//@Descrition: Returns the list of MapObjects;
		return objs;
	}
	public int getSizeX(){
		//@Description: Returns the X-Axis size of the terrain
		return this.sizeX;
	}
	public int getSizeY(){
		//@Description: Returns the Y-Axis size of the Terrain
		return this.sizeY;
	}

	public void generate_Terrain(String terrainBuffer) {
		//@Description: Receives a String with terrain information
		//@Assumes: There is a minimum of size of the terrain and a finch in the terrain
		//@Assumes: There is a correct Terrain Information in the string

		String type = "";
		Direction di = null;
		
	     StringTokenizer st = new StringTokenizer(terrainBuffer);
	     while (st.hasMoreTokens()) {
	    	 type = st.nextToken();
	         	if (type.equals("size")){

					int x = Integer.parseInt( st.nextToken() );
					int y = Integer.parseInt( st.nextToken() );
					SetSize(x, y);
					AddBorderwalls();
				
				} else if (type.equals("wall")){

					int x = Integer.parseInt( st.nextToken() );
					int y = Integer.parseInt( st.nextToken() );
					String g = st.nextToken();
					int d = Integer.parseInt( g );
					di = get_Direction(d);
					Wall a = new Wall(x, y, di );
					objs.add(a);
					if (di==Direction.NORTH){
						a = new Wall (x,y-1,Direction.SOUTH);
						if (exists(a)==false){ objs.add(a);}
						a = new Wall (x,y,Direction.NORTH);
						if (exists(a)==false){ objs.add(a);}
					} else if (di==Direction.SOUTH){
						a = new Wall (x,y+1,Direction.NORTH);
						if (exists(a)==false){ objs.add(a);}
						a = new Wall (x,y,Direction.SOUTH);
						if (exists(a)==false){ objs.add(a);}
					} else if (di==Direction.EAST){
						a = new Wall (x+1,y,Direction.WEST);
						if (exists(a)==false){ objs.add(a);}
						a = new Wall (x,y,Direction.EAST);
						if (exists(a)==false){ objs.add(a);}
					} else if (di==Direction.WEST){
						a = new Wall (x-1,y,Direction.EAST);
						if (exists(a)==false){ objs.add(a);}
						a = new Wall (x,y,Direction.WEST);
						if (exists(a)==false){ objs.add(a);}
					} 
				} else if (type.equals("beeper")){

					int x = Integer.parseInt( st.nextToken() );
					int y = Integer.parseInt( st.nextToken() );
					int r = Integer.parseInt( st.nextToken() );
					int g = Integer.parseInt( st.nextToken() );
					int b = Integer.parseInt( st.nextToken() );
					Color c = new Color(r,g,b);
					Beeper b1 = new Beeper(x, y, c);
					objs.add(b1);

				} else if (type.equals("finch")){
	
					int x = Integer.parseInt( st.nextToken() );
					int y = Integer.parseInt( st.nextToken() );
					String g = st.nextToken();
					int d = Integer.parseInt( g );
					di = get_Direction(d);
	
					Finch f = new Finch(x, y, di);
					objs.add(f);
				} 
	         
	      }
		
	}
	private boolean exists(MapObject a){
		//@Description: in combination with Find Object is used to see if a particular object is in the terrain;
		//@Assumes: a parameter is not null;
		//@Parameters: a MapObject called a
		if (FindObject(a)!=null){
				return true;
			}
		return false;
		}
	private MapObject FindObject(MapObject a) {
		Iterator<MapObject> itr = objs.iterator();

		MapObject element = null;
		
		for(int i=0; i<objs.size(); i++){

			element = itr.next();

			if (element instanceof Wall) { 
				if (((Wall)element).equals(((Wall)a))){
					return element;
					}
				}
			if (element instanceof Finch) {
				
			}
			if (element instanceof Beeper) { 

			}

		}
		return element;
	}
	private MapObject findWall(int x, int y, Direction di) {
		Iterator<MapObject> itr = objs.iterator();

		MapObject element = null;
		
		for(int i=0; i<objs.size(); i++){

			element = itr.next();

			if (element instanceof Wall) { 
				if (((Wall)element).getPositionX() == x && ((Wall)element).getPositionY() == y && ((Wall)element).getOrientation() == di){
				//	System.out.println(x + " == " + ((Wall)element).getPositionX() + " && " +  y +" == " +  ((Wall)element).getPositionY()  + " && " +   di + " == " +  ((Wall)element).getOrientation());
					return element;
					}
				}

		}
		return null;
	}
	private void AddBorderwalls() {
		// TODO Auto-generated method stub
		int x = this.sizeX;
		int y = this.sizeY;
		
		for (int i = 0; i<x;i++){
			Wall a = new Wall(i, 0, Direction.NORTH );
			objs.add(a);
			a = new Wall(i, y-1, Direction.SOUTH);
			objs.add(a);
		}
		for (int i = 0;i<y;i++){
			Wall a = new Wall(0, i, Direction.WEST );
			objs.add(a);
			a = new Wall(x-1, i, Direction.EAST);
			objs.add(a);
			
		}
	}

	private Direction get_Direction(int d) {
		// TODO Auto-generated method stub

		Direction di = null;
		if (d == 0){
			di = Direction.NORTH;

		} else if (d == 1){
			di = Direction.SOUTH;

		} else if (d == 2){
			di = Direction.EAST;

		} else if (d == 3){
			di = Direction.WEST;

		}
		
		return di;
	}

	public String toString(){
		String str = "";
		Iterator<MapObject> itr = objs.iterator();
		
		for(int i=0; i<objs.size(); i++){

			MapObject element = itr.next();

			str = str + "\n" + element.toString();
			}
		return str;
	}
	public void addbeeper(Beeper b){
		this.add(b);
	}

	public boolean removeBeeper(Point p1) {
		// TODO Auto-generated method stub
		Iterator<MapObject> itr = objs.iterator();
		MapObject tempElement = null;
		
		for(int i=0; i<objs.size(); i++){

			MapObject element = itr.next();

			 if (element instanceof Beeper){
				 if (element.getPositionX()== p1.getX() && element.getPositionY() == p1.getY()){
					 //objs.remove(element);
					 //return true;
					 tempElement = element;
				 }
			 }
			
			
		   	}
		if (tempElement!=null){
			objs.remove(tempElement);
			return true;
		}
		return false;
		
	}

	public void addFinch(Point p1) {
		// TODO Auto-generated method stub
		Finch f = null;
		f = getFinch();
		if (f==null){
			f = new Finch(p1.x, p1.y, Direction.SOUTH);
		} else {
			if (f.getPositionX()==p1.x && f.getPositionY()==p1.y){
				f.turnleft();
			} else {
				f.setPosition(p1.x, p1.y);
			}	
		}
		
		
	}

	public void addWall(Point p, int scale) {
		// TODO Auto-generated method stub
		int x = (int) p.getX();
		int y = (int) p.getY();
		int tilex = (int) p.getX()/scale;
		int tiley = (int) p.getY()/scale;
		//System.out.println("X: " + x + " Y: " + y + " Tile (" + tilex +"," + tiley + ")");
		int x1 = 0;
		int y1 = 0;
		int x2 = 0;
		int y2 = 0;
		int offset = scale/10;
		if (offset < 5) {offset = 5;}
		
		// check if click is over the north of the tile;
		x1 = (int) (tilex*scale)+offset;
		y1 = (int) (tiley*scale)-offset;
		x2 = (int) (tilex*scale)+scale-offset;
		y2 = (int) (tiley*scale)+offset;
		boolean addwall = true;
	//	System.out.println("before checking (" + x1 +"," + y1 + ") (" + x2 + "," + y2 + ")");
		if (x>x1 && y>y1 && x<x2 && y<y2){
			//System.out.println("North of tile (" + tilex +"," + tiley + ")");
			if (findWall(tilex, tiley, Direction.NORTH) != null){
				if (tiley != 0) {
					//System.out.println("Removing N wall");
					objs.remove(findWall(tilex, tiley, Direction.NORTH));
				}
				addwall = false;
			}
			if (findWall(tilex, tiley-1, Direction.SOUTH) != null){
				if (tiley != this.sizeY){
					//System.out.println("Removing S wall");
					objs.remove(findWall(tilex, tiley-1, Direction.SOUTH));
				}
				addwall = false;
			} 
			if (addwall == true) {
				//System.out.println("Adding wall");
				objs.add(new Wall(tilex, tiley, Direction.NORTH));
				objs.add(new Wall(tilex, tiley-1, Direction.SOUTH));
			}
			
			
		}
		//check if click was over the south of the tile
		x1 = (int) (tilex*scale)+offset;
		y1 = (int) (tiley*scale)+scale-offset;
		x2 = (int) (tilex*scale)+scale-offset;
		y2 = (int) (tiley*scale)+scale+offset;
		if (x>x1 && y>y1 && x<x2 && y<y2){
			//System.out.println("South of tile (" + tilex +"," + tiley + ")");
			if (findWall(tilex, tiley, Direction.SOUTH) != null){
				if (tiley != this.sizeY-1) {
					//System.out.println("Removing S wall");
					objs.remove(findWall(tilex, tiley, Direction.SOUTH));
				}
				addwall = false;
			}
			if (findWall(tilex, tiley+1, Direction.NORTH) != null){
				if (tiley != 0){
					//System.out.println("Removing N wall");
					objs.remove(findWall(tilex, tiley+1, Direction.NORTH));
				}
				addwall = false;
			} 
			if (addwall == true) {
				
				
				if (tiley != this.sizeY-1) {
					//System.out.println("Adding S wall");
					objs.add(new Wall(tilex, tiley, Direction.SOUTH));
				}
				if (tiley != 0){
					//System.out.println("Adding N wall");
					objs.add(new Wall(tilex, tiley+1, Direction.NORTH));
				}
			
			}
		}
		//check if click was over the west of the tile
		x1 = (int) (tilex*scale)-offset;
		y1 = (int) (tiley*scale)+offset;
		x2 = (int) (tilex*scale)+offset;
		y2 = (int) (tiley*scale)+scale-offset;
		if (x>x1 && y>y1 && x<x2 && y<y2){
			//System.out.println("west of tile (" + tilex +"," + tiley + ")");
			
			
			if (findWall(tilex, tiley, Direction.WEST) != null){
				if (tilex != 0) {
					//System.out.println("Removing W wall");
					objs.remove(findWall(tilex, tiley, Direction.WEST));
				}
				addwall = false;
			}
			if (findWall(tilex-1, tiley, Direction.EAST) != null){
				if (tilex != this.getSizeX()){
					//System.out.println("Removing E wall");
					objs.remove(findWall(tilex-1, tiley, Direction.EAST));
				}
				addwall = false;
			} 
			if (addwall == true) {
				
				if (tilex != 0) {
					//System.out.println("Adding W wall");
					objs.add(new Wall(tilex, tiley, Direction.WEST));
				}
				if (tilex != this.getSizeX()){
					//System.out.println("Adding E wall");
					objs.add(new Wall(tilex-1, tiley, Direction.EAST));
				}
			}
	}
		
		//check if click was over the east of the tile
		x1 = (int) (tilex*scale)+scale-offset;
		y1 = (int) (tiley*scale)+offset;
		x2 = (int) (tilex*scale)+scale+offset;
		y2 = (int) (tiley*scale)+scale-offset;
		if (x>x1 && y>y1 && x<x2 && y<y2){
			//System.out.println("East of tile (" + tilex +"," + tiley + ")");
			
			
			if (findWall(tilex, tiley, Direction.EAST) != null){
				if (tilex != this.getSizeX()) {
					//System.out.println("Removing E wall");
					objs.remove(findWall(tilex, tiley, Direction.EAST));
				}
				addwall = false;
			}
			if (findWall(tilex+1, tiley, Direction.WEST) != null){
				if (tilex != 0){
					//System.out.println("Removing W wall");
					objs.remove(findWall(tilex+1, tiley, Direction.WEST));
				}
				addwall = false;
			} 
			if (addwall == true) {
				
				if (tilex != this.getSizeX()) {
					//System.out.println("Adding E wall");
					objs.add(new Wall(tilex, tiley, Direction.EAST));
				}
				if (tilex != 0){
					//System.out.println("Adding W wall");
					objs.add(new Wall(tilex+1, tiley, Direction.WEST));
				}
			}
		}
	}

	public String getstringtosave() {
		// TODO Auto-generated method stub
		String str = "";
		str = str + "size " + this.getSizeX() + " " + this.getSizeY();
		
		Iterator<MapObject> itr = objs.iterator();

		MapObject element = null;
		
		for(int i=0; i<objs.size(); i++){

			element = itr.next();

			if (element instanceof Wall) {
				
				//quick bix no to save borders
				if (isBorder((Wall)element) == false){
					//quick fix no more time :(
					int d = 0;
					if (((Wall)element).getOrientation() == Direction.NORTH){
						d = 0;
					} else if (((Wall)element).getOrientation() == Direction.SOUTH){
						d = 1;
					} else if (((Wall)element).getOrientation() == Direction.EAST){
						d = 2;
					} else if (((Wall)element).getOrientation() == Direction.WEST){
						d = 3;
					}
					str = str + "\nwall " + element.getPositionX() + " " + element.getPositionY() + " " + d;
				}
				}
			if (element instanceof Finch) {
				// quick fix no more time :(
				
				int d = 0;
				if (((Finch)element).getOrientation() == Direction.NORTH){
					d = 0;
				} else if (((Finch)element).getOrientation() == Direction.SOUTH){
					d = 1;
				} else if (((Finch)element).getOrientation() == Direction.EAST){
					d = 2;
				} else if (((Finch)element).getOrientation() == Direction.WEST){
					d = 3;
				}
				str = str + "\nfinch " + element.getPositionX() + " " + element.getPositionY() + " " + d;
				}
			if (element instanceof Beeper) { 
				str = str + "\nbeeper " + element.getPositionX() + " " + element.getPositionY() + " "  + ((Beeper)element).getColor().getRed() + " " + ((Beeper)element).getColor().getGreen() + " " + ((Beeper)element).getColor().getBlue();  
				}

		}
		return str;
	}

	private boolean isBorder(Wall element) {
		// TODO Auto-generated method stub
		if (element.getOrientation() == Direction.NORTH && element.getPositionY() <= 0){
			return true;
		} else if (element.getOrientation() == Direction.NORTH && element.getPositionY() > this.getSizeY()-1){
			return true;
		} else if (element.getOrientation() == Direction.SOUTH && element.getPositionY() >= this.getSizeY()-1){
			return true;
		} else if (element.getOrientation() == Direction.SOUTH && element.getPositionY() < 0){
			return true;
		} else if (element.getOrientation() == Direction.EAST && element.getPositionX() >= this.getSizeX()-1){
			return true;
		} else if (element.getOrientation() == Direction.EAST && element.getPositionX() < 0){
			return true;
		} else if (element.getOrientation() == Direction.WEST && element.getPositionX() <= 0){
			return true;
		} else if (element.getOrientation() == Direction.WEST && element.getPositionX() >= this.getSizeX()-1){
			return true;
		}
		return false;
	}

	public int countBeepers(Finch finch) {
		// TODO Auto-generated method stub
		int count = 0;
		Iterator<MapObject> itr = objs.iterator();

		MapObject element = null;
		
		for(int i=0; i<objs.size(); i++){

			element = itr.next();
			if (element.getPositionX() == finch.getPositionX() && element.getPositionY() == finch.getPositionY()){
				if (element instanceof Beeper) {
					count = count + 1;
				}
			}
		}
		return count;
	}

}
